﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: DelimiterBasedFrameDecoder.cs
* 文件说明: 基于分隔符 - 数据帧解码器
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 基于分隔符 - 数据帧解码器
    /// </summary>
    public class DelimiterBasedFrameDecoder : BaseByteFrameDecoder
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="delimiters">分隔符，最好为多字节，并且数据中不会出现分隔符而造成误解码</param>
        /// <param name="maxFrameLength">最大数据长度，仅判断数据长度</param>
        public DelimiterBasedFrameDecoder(byte[] delimiters, int maxFrameLength = 0)
        {
            if (delimiters == null || delimiters.Length == 0)
            {
                throw new ArgumentNullException("Delimiters cannot be empty.");
            }

            FrameExceptDataLength = delimiters.Length;
            Delimiters = delimiters;
            MaxFrameLength = maxFrameLength;
        }

        /// <summary>
        /// 分隔符
        /// </summary>
        public byte[] Delimiters { get; }

        /// <summary>
        /// 根据数据创建完整帧数据
        /// </summary>
        /// <param name="data">数据</param>
        /// <returns>完整帧数据</returns>
        public byte[] CreateFrame(byte[] data)
        {
            byte[] bytes = new byte[data.Length + FrameExceptDataLength];
            Array.Copy(data, 0, bytes, 0, data.Length);
            Array.Copy(Delimiters, 0, bytes, data.Length, FrameExceptDataLength);
            return bytes;
        }

        /// <summary>
        /// 解析完整帧数据
        /// </summary>
        /// <param name="frame">完整帧数据</param>
        /// <param name="args">帧解码</param>
        /// <returns>解析是否成功</returns>
        public bool DecodeFrame(byte[] frame, out DelimiterByteFrameDataEventArgs args)
        {
            args = null;
            int idx = frame.AsSpan().IndexOf(Delimiters);
            if (frame.Length >= Delimiters.Length && idx >= 0 && idx + Delimiters.Length == frame.Length)
            {
                args = new DelimiterByteFrameDataEventArgs(
                    this,
                    frame.AsSpan().Slice(0, frame.Length - Delimiters.Length).ToArray(),
                    Delimiters,
                    frame);
            }

            return args != null;
        }

        /// <summary>
        /// 解码函数
        /// </summary>
        protected override void Decoding()
        {
            if (Cache.WrittenCount < FrameExceptDataLength) return;

            int index = Cache.WrittenSpan.IndexOf(Delimiters);
            while (index >= 0)
            {
                if (MinFrameLength > 0 && index < MinFrameLength)
                {
                    DecoderError("The data length is less than the minimum length", Cache.WrittenSpan.ToArray());
                    Cache.Remove(index + FrameExceptDataLength);
                    index = Cache.WrittenSpan.IndexOf(Delimiters);
                    continue;
                }

                if (MaxFrameLength > 0 && MaxFrameLength > MinFrameLength && index > MaxFrameLength)
                {
                    DecoderError("The data length is greater than the maximum length.", Cache.WrittenSpan.ToArray());
                    Cache.Remove(index + FrameExceptDataLength);
                    index = Cache.WrittenSpan.IndexOf(Delimiters);
                    continue;
                }

                LastDecodedTime = DateTime.Now;
                var e = new DelimiterByteFrameDataEventArgs(
                    this,
                    Cache.WrittenSpan.Slice(0, index).ToArray(),
                    Delimiters,
                    Cache.WrittenSpan.Slice(0, index + FrameExceptDataLength).ToArray());

                DoDecoder(this, e);
                Cache.Remove(index + FrameExceptDataLength);

                if (Cache.WrittenCount < FrameExceptDataLength) break;
                index = Cache.WrittenSpan.IndexOf(Delimiters);
            }
        }
    }
}
